import type { StyleTagOptions } from '../index'
import { logger } from '../../../types/constants'
import { SYMBOLS } from '../../../types/interfaces'
import { ToolKit } from '../../utils/toolkit'
import { StyleBaseInjector } from '../base'

/**
 * iframe CSS 注入配置接口
 */
export interface IframeInjectionConfig {
  /** 目标 iframe 选择器 */
  iframeSelector: string
  /** 跨域处理策略 */
  crossOriginStrategy: 'message' | 'overlay' | 'both'
  /** 同源 iframe 注入时机 */
  injectionTiming: 'immediate' | 'onload' | 'dom-ready'
  /** 是否监听动态 iframe */
  observeDynamicIframes: boolean
  /** 最大重试次数 */
  maxRetries: number
  /** 重试延迟时间(ms) */
  retryDelay: number
  /** 自定义 iframe 过滤器 */
  iframeFilter?: (iframe: HTMLIFrameElement) => boolean
}

/**
 * iframe CSS 注入器
 */
export class IframeInjector extends StyleBaseInjector {
  readonly name: string = 'IframeInjector'
  private readonly config: IframeInjectionConfig = {
    iframeSelector: 'iframe',
    crossOriginStrategy: 'both',
    injectionTiming: 'onload',
    observeDynamicIframes: true,
    maxRetries: 3,
    retryDelay: 100,
  }

  readonly styleTags: Map<string, Map<string, HTMLStyleElement>> = new Map()
  private mutationObserver?: MutationObserver
  private readonly retryCounters: Map<string, number> = new Map()

  constructor(config?: IframeInjectionConfig) {
    super()
    this.config = { ...this.config, ...config }
  }

  destroy(): void {
    this.styleTags.forEach((iframeMap) => {
      iframeMap.forEach(style => style.remove())
      iframeMap.clear()
    })
    this.styleTags.clear()
    this.retryCounters.clear()
    this.stop()
  }

  createStyleTag(
    id: string,
    content: string,
    options?: StyleTagOptions,
  ): HTMLStyleElement | null {
    const {
      onlyCreate = false,
      allowEmpty = false,
      targetElement,
    } = options || {}

    if (!this.checkContentValidity(id, content, onlyCreate, allowEmpty))
      return null

    const style = document.createElement('style')
    style.id = this.generateStyleId(id)
    style.setAttribute('data-scope', 'iframe')

    if (onlyCreate) {
      logger.debug(`[${this.name}] ${SYMBOLS.UI.BOOKMARK} 创建 iframe 空样式标签: id='${id}'`)
      return style
    }

    style.textContent = this.processStyleContent(content)

    if (targetElement && this.isIframeElement(targetElement as HTMLStyleElement)) {
      this.injectToSingleIframe(style, targetElement as HTMLIFrameElement, options)
    }
    else {
      this.injectToAllIframes(style, options)
    }

    return style
  }

  removeStyleTag(id: string): boolean {
    let removedCount = 0

    this.styleTags.forEach((iframeMap, _iframeId) => {
      const style = iframeMap.get(id)
      if (style) {
        style.remove()
        iframeMap.delete(id)
        removedCount++
      }
    })

    // 清理重试计数器
    this.retryCounters.delete(id)

    if (removedCount > 0) {
      logger.success(`[${this.name}] ${SYMBOLS.UI.BOOKMARK} 从 ${removedCount} 个 iframe 中移除样式: id='${id}'`)
      return true
    }

    logger.warn(`[${this.name}] iframe 样式不存在: id='${id}'`)
    return false
  }

  injectStyle(style: HTMLStyleElement, options: StyleTagOptions = {}): boolean {
    const { targetElement, retryCount = 0 } = options

    ToolKit.ui.style.markBaiduSearchElement(style)
    if (targetElement && this.isIframeElement(targetElement as HTMLStyleElement)) {
      return this.injectToSingleIframeWithRetry(style, targetElement as HTMLIFrameElement, options, retryCount)
    }
    else {
      this.injectToAllIframes(style, options)
      return true
    }
  }

  updateStyle(id: string, content: string, _options: StyleTagOptions = {}): boolean {
    let updatedCount = 0

    this.styleTags.forEach((iframeMap, _iframeId) => {
      const style = iframeMap.get(id)
      if (style) {
        style.textContent = this.processStyleContent(content)
        updatedCount++
      }
    })

    if (updatedCount > 0) {
      logger.success(`[${this.name}] ${SYMBOLS.UI.BOOKMARK} 更新 ${updatedCount} 个 iframe 中的样式: id='${id}'`)
      return true
    }

    logger.warn(`[${this.name}] iframe 样式不存在: id='${id}'`)
    return false
  }

  /**
   * 开始监听和处理 iframe
   */
  start(): void {
    this.processExistingIframes()

    if (this.config.observeDynamicIframes) {
      this.startDynamicObservation()
    }
  }

  /**
   * 停止监听
   */
  stop(): void {
    if (this.mutationObserver) {
      this.mutationObserver.disconnect()
      this.mutationObserver = undefined
    }
  }

  /**
   * 带重试机制的单个 iframe 注入
   */
  private injectToSingleIframeWithRetry(
    style: HTMLStyleElement,
    iframe: HTMLIFrameElement,
    options: StyleTagOptions = {},
    retryCount: number = 0,
  ): boolean {
    const success = this.injectToSingleIframe(style, iframe, options)

    if (!success && retryCount < this.config.maxRetries) {
      const retryKey = `${this.getIframeId(iframe)}-${style.id}`
      const currentRetry = this.retryCounters.get(retryKey) || 0

      if (currentRetry < this.config.maxRetries) {
        this.retryCounters.set(retryKey, currentRetry + 1)

        setTimeout(() => {
          logger.debug(`[${this.name}] ${SYMBOLS.STATUS.REFRESH} 重试注入 (${currentRetry + 1}/${this.config.maxRetries}): ${style.id}`)
          this.injectToSingleIframeWithRetry(style, iframe, options, retryCount + 1)
        }, this.config.retryDelay * 2 ** currentRetry) // 指数退避

        return false
      }
    }

    if (success) {
      this.retryCounters.delete(`${this.getIframeId(iframe)}-${style.id}`)
    }

    return success
  }

  private injectToSingleIframe(
    style: HTMLStyleElement,
    iframe: HTMLIFrameElement,
    options: StyleTagOptions = {},
  ): boolean {
    const { override = true } = options
    const id = style.id

    try {
      // 应用 iframe 过滤器
      if (this.config.iframeFilter && !this.config.iframeFilter(iframe)) {
        logger.debug(`[${this.name}] iframe 被过滤器跳过: ${this.getIframeId(iframe)}`)
        return false
      }

      // 检查注入时机
      if (!this.shouldInjectNow(iframe)) {
        this.scheduleInjection(style, iframe, options)
        return false
      }

      if (!iframe.contentDocument || !iframe.contentWindow) {
        this.scheduleInjection(style, iframe, options)
        return false
      }

      const iframeDoc = iframe.contentDocument

      try {
        const existingStyle = iframeDoc.getElementById(id) as HTMLStyleElement | null

        if (existingStyle) {
          if (!override) {
            logger.hint(`[${this.name}] iframe 样式已存在: id='${id}'`)
            return false
          }
          existingStyle.remove()
          logger.hint(`[${this.name}] ${SYMBOLS.OTHERS.CLEAR} 已移除旧样式: id='${id}'`)
        }

        iframeDoc.head.appendChild(style.cloneNode(true))
        this.trackIframeStyle(iframe, id, style)
        logger.success(`[${this.name}] ${SYMBOLS.UI.BOOKMARK} iframe 样式注入成功: id='${id}'`)
        return true
      }
      catch {
        logger.warn(`[${this.name}] 同源策略限制，尝试跨域通信: id='${id}'`)
        return this.handleCrossOriginInjection(style, iframe)
      }
    }
    catch (error) {
      logger.error(`[${this.name}] iframe 样式注入失败: id='${id}'`, error)
      return false
    }
  }

  /**
   * 处理跨域注入
   */
  private handleCrossOriginInjection(
    style: HTMLStyleElement,
    iframe: HTMLIFrameElement,
  ): boolean {
    const strategies = {
      message: () => this.injectViaMessagePassing(style, iframe),
      overlay: () => this.injectViaOverlay(style, iframe),
      both: () => {
        const messageSuccess = this.injectViaMessagePassing(style, iframe)
        const overlaySuccess = this.injectViaOverlay(style, iframe)
        return messageSuccess || overlaySuccess
      },
    }

    const strategy = strategies[this.config.crossOriginStrategy]
    return strategy ? strategy() : false
  }

  /**
   * 检查是否应该立即注入
   */
  private shouldInjectNow(iframe: HTMLIFrameElement): boolean {
    switch (this.config.injectionTiming) {
      case 'immediate':
        return true
      case 'onload':
        return iframe.contentDocument?.readyState === 'complete'
      case 'dom-ready':
        return iframe.contentDocument?.readyState === 'interactive'
          || iframe.contentDocument?.readyState === 'complete'
      default:
        return true
    }
  }

  /**
   * 调度延迟注入
   */
  private scheduleInjection(style: HTMLStyleElement, iframe: HTMLIFrameElement, options: StyleTagOptions): void {
    const eventType = this.config.injectionTiming === 'onload' ? 'load' : 'DOMContentLoaded'

    iframe.addEventListener(eventType, () => {
      this.injectToSingleIframe(style, iframe, options)
    }, { once: true })
  }

  private injectToAllIframes(style: HTMLStyleElement, options: StyleTagOptions = {}): void {
    const iframes = Array.from(document.querySelectorAll(this.config.iframeSelector))
      .filter(iframe => this.isIframeElement(iframe))
      .filter(iframe => !this.config.iframeFilter || this.config.iframeFilter(iframe as HTMLIFrameElement))

    let successCount = 0

    iframes.forEach((iframe, index) => {
      const iframeStyle = style.cloneNode(true) as HTMLStyleElement
      iframeStyle.id = `${style.id}-${index}`

      if (this.injectToSingleIframe(iframeStyle, iframe as HTMLIFrameElement, options))
        successCount++
    })

    logger.success(`[${this.name}] ${SYMBOLS.UI.BOOKMARK} 已向 ${successCount}/${iframes.length} 个 iframe 注入样式`)
  }

  private injectViaMessagePassing(style: HTMLStyleElement, iframe: HTMLIFrameElement): boolean {
    try {
      const message = {
        type: 'style-injection',
        id: style.id,
        content: style.textContent,
        action: 'add',
        timestamp: Date.now(),
      }

      iframe.contentWindow!.postMessage(message, '*')
      logger.hint(`[${this.name}] 发送跨域样式注入请求: ${style.id}`)
      return true
    }
    catch (error) {
      logger.error(`[${this.name}] 跨域样式注入失败: ${style.id}`, error)
      return false
    }
  }

  private injectViaOverlay(style: HTMLStyleElement, iframe: HTMLIFrameElement): boolean {
    try {
      // 创建覆盖层样式
      const overlayStyle = document.createElement('style')
      overlayStyle.textContent = `
        [data-iframe-overlay="${iframe.id}"] {
          ${style.textContent}
        }
      `
      document.head.appendChild(overlayStyle)
      logger.info(`[${this.name}] 创建覆盖层样式: ${style.id}`)
      return true
    }
    catch (error) {
      logger.error(`[${this.name}] 覆盖层样式注入失败: ${style.id}`, error)
      return false
    }
  }

  private processExistingIframes(): void {
    const iframes = Array.from(document.querySelectorAll(this.config.iframeSelector))
      .filter(iframe => this.isIframeElement(iframe))
      .filter(iframe => !this.config.iframeFilter || this.config.iframeFilter(iframe as HTMLIFrameElement))

    logger.debug(`[${this.name}] 处理现有 ${iframes.length} 个 iframe`)

    iframes.forEach((iframe, index) => {
      this.processSingleIframe(iframe as HTMLIFrameElement, index)
    })
  }

  private processSingleIframe(iframe: HTMLIFrameElement, index: number): void {
    if (!this.shouldInjectNow(iframe)) {
      this.scheduleInjection(this.createDefaultStyle(), iframe, {})
      return
    }

    this.injectToIframe(iframe, index)
  }

  private startDynamicObservation(): void {
    this.mutationObserver = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        mutation.addedNodes.forEach((node) => {
          if (node.nodeType === Node.ELEMENT_NODE && (node as Element).nodeName === 'IFRAME') {
            const iframe = node as HTMLIFrameElement
            if (this.isIframeElement(iframe) && (!this.config.iframeFilter || this.config.iframeFilter(iframe))) {
              this.processSingleIframe(iframe, -1)
            }
          }
        })
      })
    })

    this.mutationObserver.observe(document.body, { childList: true, subtree: true })
    logger.debug(`[${this.name}] ${SYMBOLS.STATUS.INFO} 开始监听动态 iframe`)
  }

  private trackIframeStyle(iframe: HTMLIFrameElement, styleId: string, style: HTMLStyleElement): void {
    const iframeId = this.getIframeId(iframe)
    if (!this.styleTags.has(iframeId)) {
      this.styleTags.set(iframeId, new Map())
    }
    this.styleTags.get(iframeId)!.set(styleId, style)
  }

  private getIframeId(iframe: HTMLIFrameElement): string {
    return iframe.id || iframe.src || `iframe-${Date.now()}`
  }

  private isIframeElement(element: Element): element is HTMLIFrameElement {
    return element.tagName === 'IFRAME'
  }

  getStyleTag(id: string): HTMLStyleElement | null {
    const styleId = this.generateStyleId(id)
    // 遍历所有 iframe 中的样式
    for (const iframeMap of this.styleTags.values()) {
      const style = iframeMap.get(styleId)
      if (style) {
        return style
      }
    }

    return null
  }

  generateStyleId(id: string): string {
    return `iframe-${id}`
  }

  private createDefaultStyle(): HTMLStyleElement {
    const style = document.createElement('style')
    style.textContent = this.generateDefaultCSS()
    return style
  }

  private injectToIframe(iframe: HTMLIFrameElement, _index: number): void {
    const style = this.createDefaultStyle()
    this.injectToSingleIframe(style, iframe, {})
  }

  private generateDefaultCSS(): string {
    return `
      .ad-container { display: none !important; }
      .popup-ad { visibility: hidden !important; }
      [data-ad] { opacity: 0 !important; }
    `
  }

  /**
   * 获取注入统计
   */
  getInjectionStats(): {
    totalIframes: number
    totalStyles: number
    activeRetries: number
  } {
    return {
      totalIframes: this.styleTags.size,
      totalStyles: Array.from(this.styleTags.values()).reduce((sum, map) => sum + map.size, 0),
      activeRetries: this.retryCounters.size,
    }
  }
}
